package com.alibaba.csp.sentinel.dashboard.rule.apollo;

import com.alibaba.csp.sentinel.dashboard.rule.AbstractDynamicRuleService;
import com.alibaba.csp.sentinel.dashboard.rule.apollo.config.ApolloProperties;
import com.alibaba.csp.sentinel.util.AssertUtil;
import com.ctrip.framework.apollo.openapi.client.ApolloOpenApiClient;
import com.ctrip.framework.apollo.openapi.dto.NamespaceReleaseDTO;
import com.ctrip.framework.apollo.openapi.dto.OpenItemDTO;
import com.ctrip.framework.apollo.openapi.dto.OpenNamespaceDTO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;

import java.util.List;
import java.util.Objects;

/**
 * DynamicRuleApolloService
 *
 * @author kancy
 * @date 2020/5/4 15:57
 */
public abstract class DynamicRuleApolloService<T> extends AbstractDynamicRuleService<T> {

    @Autowired
    private ApolloProperties apolloProperties;
    @Autowired
    protected ApolloOpenApiClient apolloOpenApiClient;

    @Override
    public List<T> getRules(String appName) throws Exception {
        OpenNamespaceDTO openNamespaceDTO = apolloOpenApiClient.getNamespace(apolloProperties.getAppId(),
                getEnv(appName), apolloProperties.getClusterName(), getNamespaceName(appName));
        String rules = openNamespaceDTO
                .getItems()
                .stream()
                .filter(p -> p.getKey().equals(getDataId(appName)))
                .map(OpenItemDTO::getValue)
                .findFirst()
                .orElse("");
        return deserialize(rules);
    }
    /**
     * Publish rules to remote rule configuration center for given application name.
     *
     * @param app   app name
     * @param rules list of rules to push
     * @throws Exception if some error occurs
     */
    @Override
    public void publish(String app, List<T> rules) throws Exception {

        AssertUtil.notEmpty(app, "app name cannot be empty");
        if (Objects.isNull(rules)) {
            return;
        }

        // Create or update item
        OpenItemDTO openItemDTO = new OpenItemDTO();
        openItemDTO.setKey(getDataId(app));
        openItemDTO.setValue(serialize(rules));
        openItemDTO.setComment("Modify or add configurations by sentinel dashboard");
        openItemDTO.setDataChangeCreatedBy(apolloProperties.getDataChangeCreatedBy());
        apolloOpenApiClient.createOrUpdateItem(apolloProperties.getAppId(),
                getEnv(app), apolloProperties.getClusterName(), getNamespaceName(app), openItemDTO);

        // Release configuration
        NamespaceReleaseDTO namespaceReleaseDTO = new NamespaceReleaseDTO();
        namespaceReleaseDTO.setEmergencyPublish(true);
        namespaceReleaseDTO.setReleasedBy(apolloProperties.getDataChangeCreatedBy());
        namespaceReleaseDTO.setReleaseTitle("Modify or add configurations by sentinel dashboard");
        apolloOpenApiClient.publishNamespace(apolloProperties.getAppId(),
                getEnv(app), apolloProperties.getClusterName(), getNamespaceName(app), namespaceReleaseDTO);
    }

    private String getEnv(String app) {
        if (StringUtils.hasText(apolloProperties.getEnv())){
            return apolloProperties.getEnv();
        }
        if (app.indexOf("-") > 0){
            String env = app.substring(app.indexOf("-") + 1);
            if (StringUtils.hasText(env)){
                return env.toUpperCase();
            }
        }
        throw new IllegalArgumentException("Apollo Env Unconfirmed.");
    }

    /**
     * 命名空间
     * 默认文件格式为properties
     * @param appName
     * @return
     */
    protected String getNamespaceName(String appName) {
        return appName;
    }

    /**
     * 对应的属性key
     * @param appName
     * @return
     */
    protected abstract String getDataId(String appName);

    /**
     * 获取外部化配置
     * @return
     */
    public ApolloProperties getApolloProperties() {
        return apolloProperties;
    }
}
